﻿
namespace XUCore.WeChat.Enterprise.Net.ServerMessages
{
    using Microsoft.Extensions.Logging;
    using System;
    using System.Threading.Tasks;
    using Microsoft.Extensions.DependencyInjection;
    using XUCore.WeChat.Net.ServerMessages.To;
    using XUCore.WeChat.Net.ServerMessages.From;
    using System.Xml.Linq;
    using XUCore.WeChat.Helper;
    using XUCore.WeChat.Enterprise.Net.ServerMessages.From;

    /// <summary>
    /// 服务器消息处理类
    /// </summary>
    public class EnterpriseServerMessageHandler
    {
        private readonly EnterpriseWeChatFuncs wxFuncs;
        private readonly ILogger<EnterpriseServerMessageHandler> logger;
        private readonly IServiceProvider serviceProvider;

        public EnterpriseServerMessageHandler(EnterpriseWeChatFuncs wxFuncs, ILogger<EnterpriseServerMessageHandler> logger, IServiceProvider serviceProvider)
        {
            this.wxFuncs = wxFuncs;
            this.logger = logger;
            this.serviceProvider = serviceProvider;
        }

        /// <summary>
        /// 处理消息
        /// </summary>
        /// <param name="suiteId">第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id（对应于旧的以tj开头的套件id）；代开发应用以dk开头</param>
        /// <param name="corpId"></param>
        /// <param name="agentId"></param>
        /// <param name="xmlStr">XML字符串</param>
        /// <returns>The <see cref="Task{ToMessageBase}"/></returns>
        public async Task<ToMessageBase> HandleMessage(string suiteId, string corpId, int agentId, string xmlStr)
        {
            ToMessageBase toMessage = new ToNullMessage();

            logger.LogDebug(Environment.NewLine + xmlStr);
            var xmlElement = XElement.Parse(xmlStr);
            var msgTypeElement = xmlElement.Element("MsgType");

            if (string.IsNullOrWhiteSpace(msgTypeElement?.Value))
            {
                throw new WeChatSdkException("消息类型不能为空");
            }

            var msgType = msgTypeElement.Value.Trim().ToLower();
            //接收的消息
            IFromMessage fromMessage = null;
            //处理结果
            Tuple<ToMessageBase, IFromMessage> resulTuple = default;

            // 处理事件消息
            if (msgType == "event")
            {
                var fromEventTypeElement = xmlElement.Element("Event");

                if (string.IsNullOrWhiteSpace(fromEventTypeElement?.Value)) throw new WeChatSdkException("事件类型不能为空");
                var fromEvent = fromEventTypeElement.Value.Trim().ToLower();
                //记录日志
                logger.LogDebug("Event " + fromEvent);
                //处理微信服务器事件Key大小写不一致的问题
                xmlStr = xmlStr.Replace("<Event><![CDATA[" + fromEventTypeElement.Value + "]]></Event>", "<Event><![CDATA[" + fromEvent + "]]></Event>");

                // var fromEventType = (FromEventTypes)Enum.Parse(typeof(FromEventTypes), fromEvent);
                if (!Enum.TryParse<FromEventTypes>(fromEvent, out var fromEventType))
                {
                    logger.LogWarning($"{nameof(FromEventTypes)} ==> {fromEvent} 事件类型不存在\r\n{xmlStr}");

                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                }
                else
                {
                    switch (fromEventType)
                    {
                        case FromEventTypes.subscribe:
                            resulTuple = await ExecuteHandler<FromSubscribeEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.unsubscribe:
                            resulTuple = await ExecuteHandler<FromUnsubscribeEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.location:
                            resulTuple = await ExecuteHandler<FromLocationEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.click:
                            resulTuple = await ExecuteHandler<FromClickEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.view:
                            resulTuple = await ExecuteHandler<FromViewEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.scan:
                            resulTuple = await ExecuteHandler<FromScanEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.enter_agent:
                            resulTuple = await ExecuteHandler<FromEnterAgentEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.batch_job_result:
                            resulTuple = await ExecuteHandler<FromBatchJobResultEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.scancode_push:
                            resulTuple = await ExecuteHandler<FromScanCodePushEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.scancode_waitmsg:
                            resulTuple = await ExecuteHandler<FromScanCodeWaitmsgEvent>(suiteId, corpId, agentId, xmlStr);
                            break;
                        case FromEventTypes.change_external_contact:
                            {
                                var fromChangeTypeElement = xmlElement.Element("ChangeType").Value.Trim().ToLower();

                                // var fromChangeType = (ChangeExternalContacChangeType)Enum.Parse(typeof(ChangeExternalContacChangeType), fromChangeTypeElement);
                                if (!Enum.TryParse<ChangeExternalContacChangeType>(fromChangeTypeElement, out var fromChangeType))
                                {
                                    logger.LogWarning($"{nameof(ChangeExternalContacChangeType)} ==> {fromChangeTypeElement} 事件类型不存在\r\n{xmlStr}");

                                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                                }
                                else
                                {
                                    resulTuple = fromChangeType switch
                                    {
                                        ChangeExternalContacChangeType.add_external_contact => await ExecuteHandler<FromChangeExternalContactAddExternalContactEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalContacChangeType.edit_external_contact => await ExecuteHandler<FromChangeExternalContactEditExternalContactEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalContacChangeType.add_half_external_contact => await ExecuteHandler<FromChangeExternalContactAddHalfExternalContactEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalContacChangeType.del_external_contact => await ExecuteHandler<FromChangeExternalContactDelExternalContactEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalContacChangeType.del_follow_user => await ExecuteHandler<FromChangeExternalContactDelFollowUserEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalContacChangeType.transfer_fail => await ExecuteHandler<FromChangeExternalContactTransferFailEvent>(suiteId, corpId, agentId, xmlStr),
                                        _ => null
                                    };
                                }
                            }
                            break;
                        case FromEventTypes.change_contact:
                            {
                                var fromChangeTypeElement = xmlElement.Element("ChangeType").Value.Trim().ToLower();

                                // var fromChangeType = (ChangeContacChangeType)Enum.Parse(typeof(ChangeContacChangeType), fromChangeTypeElement);
                                if (!Enum.TryParse<ChangeContacChangeType>(fromChangeTypeElement, out var fromChangeType))
                                {
                                    logger.LogWarning($"{nameof(ChangeContacChangeType)} ==> {fromChangeTypeElement} 事件类型不存在\r\n{xmlStr}");

                                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                                }
                                else
                                {
                                    resulTuple = fromChangeType switch
                                    {
                                        ChangeContacChangeType.create_user => await ExecuteHandler<FromChangeContactAddUserEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeContacChangeType.update_user => await ExecuteHandler<FromChangeContactEditUserEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeContacChangeType.delete_user => await ExecuteHandler<FromChangeContactDelUserEvent>(suiteId, corpId, agentId, xmlStr),
                                        _ => null
                                    };
                                }
                            }
                            break;
                        case FromEventTypes.change_external_chat:
                            {
                                var fromChangeTypeElement = xmlElement.Element("ChangeType").Value.Trim().ToLower();
                                // var fromChangeType = (ChangeExternalChatChangeType)Enum.Parse(typeof(ChangeExternalChatChangeType), fromChangeTypeElement);

                                if (!Enum.TryParse<ChangeExternalChatChangeType>(fromChangeTypeElement, out var fromChangeType))
                                {
                                    logger.LogWarning($"{nameof(ChangeExternalChatChangeType)} ==> {fromChangeTypeElement} 事件类型不存在\r\n{xmlStr}");

                                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                                }
                                else
                                {
                                    resulTuple = fromChangeType switch
                                    {
                                        ChangeExternalChatChangeType.create => await ExecuteHandler<FromChangeExternalChatCreateEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalChatChangeType.update => await ExecuteHandler<FromChangeExternalChatUpdateEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalChatChangeType.dismiss => await ExecuteHandler<FromChangeExternalChatDismissEvent>(suiteId, corpId, agentId, xmlStr),
                                        _ => null
                                    };
                                }
                            }
                            break;
                        case FromEventTypes.change_external_tag:
                            {
                                var fromChangeTypeElement = xmlElement.Element("ChangeType").Value.Trim().ToLower();
                                // var fromChangeType = (ChangeExternalTagChangeType)Enum.Parse(typeof(ChangeExternalTagChangeType), fromChangeTypeElement);

                                if (!Enum.TryParse<ChangeExternalTagChangeType>(fromChangeTypeElement, out var fromChangeType))
                                {
                                    logger.LogWarning($"{nameof(ChangeExternalTagChangeType)} ==> {fromChangeTypeElement} 事件类型不存在\r\n{xmlStr}");

                                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                                }
                                else
                                {
                                    resulTuple = fromChangeType switch
                                    {
                                        ChangeExternalTagChangeType.create => await ExecuteHandler<FromChangeExternalTagCreateEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalTagChangeType.update => await ExecuteHandler<FromChangeExternalTagUpdateEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalTagChangeType.delete => await ExecuteHandler<FromChangeExternalTagDeleteEvent>(suiteId, corpId, agentId, xmlStr),
                                        ChangeExternalTagChangeType.shuffle => await ExecuteHandler<FromChangeExternalTagShuffleEvent>(suiteId, corpId, agentId, xmlStr),
                                        _ => null
                                    };
                                }
                            }
                            break;
                        case FromEventTypes.customer_acquisition:
                            {
                                var fromChangeTypeElement = xmlElement.Element("ChangeType").Value.Trim().ToLower();
                                // var fromChangeType = (CustomerAcquisitionChangeType)Enum.Parse(typeof(CustomerAcquisitionChangeType), fromChangeTypeElement);

                                if (!Enum.TryParse<CustomerAcquisitionChangeType>(fromChangeTypeElement, out var fromChangeType))
                                {
                                    logger.LogWarning($"{nameof(CustomerAcquisitionChangeType)} ==> {fromChangeTypeElement} 事件类型不存在\r\n{xmlStr}");

                                    resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                                }
                                else
                                {
                                    resulTuple = fromChangeType switch
                                    {
                                        CustomerAcquisitionChangeType.balance_low => await ExecuteHandler<FromCustomerAcquisitionBalanceLowEvent>(suiteId, corpId, agentId, xmlStr),
                                        CustomerAcquisitionChangeType.balance_exhausted => await ExecuteHandler<FromCustomerAcquisitionBalanceExhaustedEvent>(suiteId, corpId, agentId, xmlStr),
                                        CustomerAcquisitionChangeType.link_unavailable => await ExecuteHandler<FromCustomerAcquisitionLinkUnavailableEvent>(suiteId, corpId, agentId, xmlStr),
                                        CustomerAcquisitionChangeType.customer_start_chat => await ExecuteHandler<FromCustomerAcquisitionCustomerStartChatEvent>(suiteId, corpId, agentId, xmlStr),
                                        CustomerAcquisitionChangeType.delete_link => await ExecuteHandler<FromCustomerAcquisitionDeleteLinkEvent>(suiteId, corpId, agentId, xmlStr),
                                        CustomerAcquisitionChangeType.friend_request => await ExecuteHandler<FromCustomerAcquisitionFriendRequestEvent>(suiteId, corpId, agentId, xmlStr),
                                        _ => null
                                    };
                                }
                            }
                            break;
                        default:
                            resulTuple = await ExecuteEventHandler(suiteId, fromEvent, corpId, agentId, xmlStr);
                            break;
                    }
                }
            }
            else
            {
                //记录日志
                logger.LogDebug(msgType);
                //处理会话消息
                // var fromMessageType = (FromMessageTypes)Enum.Parse(typeof(FromMessageTypes), msgType);

                if (!Enum.TryParse<FromMessageTypes>(msgType, out var fromMessageType))
                {
                    logger.LogWarning($"{nameof(FromMessageTypes)} ==> {msgType} 事件类型不存在\r\n{xmlStr}");
                }
                else
                {
                    resulTuple = fromMessageType switch
                    {
                        FromMessageTypes.text => await ExecuteHandler<FromTextMessage>(suiteId, corpId, agentId, xmlStr),
                        FromMessageTypes.image => await ExecuteHandler<FromImageMessage>(suiteId, corpId, agentId, xmlStr),
                        FromMessageTypes.voice => await ExecuteHandler<FromVoiceMessage>(suiteId, corpId, agentId, xmlStr),
                        FromMessageTypes.video => await ExecuteHandler<FromVideoMessage>(suiteId, corpId, agentId, xmlStr),
                        FromMessageTypes.location => await ExecuteHandler<FromLocationMessage>(suiteId, corpId, agentId, xmlStr),
                        FromMessageTypes.link => await ExecuteHandler<FromLinkMessage>(suiteId, corpId, agentId, xmlStr),
                        _ => throw new NotSupportedException("暂不支持类型为[" + msgType + "]的消息类型"),
                    };
                }
            }

            if (resulTuple != null)
            {
                fromMessage = resulTuple.Item2;
                toMessage = resulTuple.Item1;
            }

            if (toMessage is ToNewsMessage)
            {
                var news = toMessage as ToNewsMessage;
                if (news.Articles.Count > 10)
                    throw new NotSupportedException("图文消息不能超过10条");
                if (news.Articles.Count == 0)
                    throw new WeChatSdkException("至少需要包含一条图文消息");
                news.ArticleCount = news.Articles.Count;
            }
            if (toMessage is ToNullMessage)
            {
                return null;
            }
            if (toMessage != null && toMessage.CreateTimestamp == default)
            {
                //设置时间戳
                toMessage.CreateTimestamp = toMessage.CreateDateTime.ConvertToTimeStamp();
                toMessage.FromUserName = fromMessage.ToUserName;
                toMessage.ToUserName = fromMessage.FromUserName;
            }

            return await Task.FromResult(toMessage);
        }

        /// <summary>
        /// 执行处理函数
        /// </summary>
        /// <typeparam name="T">接受类型</typeparam>
        /// <param name="suiteId">第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id（对应于旧的以tj开头的套件id）；代开发应用以dk开头</param>
        /// <param name="corpId">企业id</param>
        /// <param name="agentId">应用id</param>
        /// <param name="xmlStr">XML字符串</param>
        /// <returns></returns>
        private async Task<Tuple<ToMessageBase, IFromMessage>> ExecuteHandler<T>(string suiteId, string corpId, int agentId, string xmlStr)
            where T : class, IFromMessage
        {
            ToMessageBase toMessage = new ToNullMessage();
            var type = typeof(T);
            var handler = serviceProvider.GetService<IEnterpriseWeChatEventHandler>();
            if (handler == null)
            {
                throw new WeChatSdkException($"接收企业微信服务端事件消息需要先注册 {nameof(IEnterpriseWeChatEventHandler)} ！");
            }
            try
            {
                IFromMessage fromMessage = XmlHelper.DeserializeObject<T>(xmlStr);
                if (fromMessage != null)
                    toMessage = await handler.Execute(suiteId, corpId, agentId, fromMessage);
                else
                    logger.LogWarning($"序列化类型【{type.FullName}】失败");

                return new Tuple<ToMessageBase, IFromMessage>(toMessage, fromMessage);
            }
            catch (Exception ex)
            {
                logger.LogError($"序列化类型【{type.FullName}】失败，{ex.Message}\r\n{xmlStr}");

                return new Tuple<ToMessageBase, IFromMessage>(toMessage, null);
            }
        }
        /// <summary>
        /// 自定义序列化事件，方便定制接入其他事件
        /// </summary>
        /// <param name="suiteId">第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id（对应于旧的以tj开头的套件id）；代开发应用以dk开头</param>
        /// <param name="fromEvent">事件类型</param>
        /// <param name="corpId">企业id</param>
        /// <param name="agentId">应用id</param>
        /// <param name="xmlStr">XML字符串</param>
        /// <returns></returns>
        private async Task<Tuple<ToMessageBase, IFromMessage>> ExecuteEventHandler(string suiteId, string fromEvent, string corpId, int agentId, string xmlStr)
        {
            ToMessageBase toMessage = new ToNullMessage();

            var handler = serviceProvider.GetService<IEnterpriseWeChatEventHandler>();
            if (handler == null)
            {
                throw new WeChatSdkException($"接收企业微信服务端事件消息需要先注册 {nameof(IEnterpriseWeChatEventHandler)} ！");
            }

            IFromMessage fromMessage = await handler.ExecuteEventHandler(suiteId, corpId, agentId, fromEvent, xmlStr);

            if (fromMessage != null)
                toMessage = await handler.Execute(suiteId, corpId, agentId, fromMessage);
            else
                throw new NotSupportedException("暂不支持类型为[" + fromEvent + "]的事件类型");

            return new Tuple<ToMessageBase, IFromMessage>(toMessage, fromMessage);
        }
    }
}